// Mix-C part
#define xuser mixc::powerful_cat::inc
#include"configure/env.hpp"
#include"extern/fs/miaofs/basic_define.hpp"
#include"extern/fs/miaofs/default_cache_level.hpp"
#include"extern/fs/miaofs/default_handle_level.hpp"
#include"extern/fs/miaofs/idisk_ops.hpp"
#include"extern/fs/miaofs.hpp"
#include"io/dir.hpp"
#include"io/file.hpp"
#include"io/tty.hpp"
#include"lang/cxx/is_ends_with.hpp"
#include"lang/cxx/strcat.hpp"
#include"lang/cxx/format.hpp"
#include"lang/cxx.hpp"
#include"math/minimum.hpp"
#include"mixc.hpp"
#include"utils/temp_allocator.hpp"
using namespace mixc::powerful_cat;


constexpr auto min_disk_bytes = 2 * 1024 * 1024;
constexpr auto max_copy_bytes = 2 * 1024 * 1024;
const u08 erase_block[inc::bytes_of_block] = {};


struct disk_ops : inc::idisk_ops{
    inc::file disk;

    uxx block_erase(aid b_begin, uxx total_block) override {
        disk.seek(b_begin * inc::bytes_of_block);

        for(uxx i = 0; i < total_block; i++){
            disk.write(erase_block, sizeof(erase_block));
        }
        return total_block;
    }

    // raw 块读取，每个块 inc::bytes_of_block 字节，要求 bytes 是 inc::bytes_of_block 的整数倍
    uxx block_read(aid b_block, void * buffer, uxx bytes) override {
        disk.seek(b_block * inc::bytes_of_block);
        return disk.read(buffer, bytes);
    }

    // raw 块写入，每个块 inc::bytes_of_block 字节，要求 bytes 是 inc::bytes_of_block 的整数倍
    uxx block_write(aid b_block, const void * buffer, uxx bytes) override {
        disk.seek(b_block * inc::bytes_of_block);
        return disk.write(buffer, bytes);
    }
};

inc::default_cache_level<disk_ops>              cache_level;
inc::default_handle_level<64/*可用句柄个数*/>   handle_level;
inc::miaofs fs(&cache_level, &handle_level);
using miaofs_itr            = inc::miaofs::entry_iterator;
using result_loop_t         = inc::miaofs::result<loop_t>;

#define xmiao_loop(EXEC,...)                                                                \
    {                                                                                       \
        result_loop_t result(inc::miaofs_code_t::success);                                  \
        while((result = (EXEC)) == loop_t::go_on);                                          \
        if (result.code() != inc::miaofs_code_t::success){                                  \
            inc::tty.write_line("ops failed, code:\v line:\v", result.code(), __LINE__);    \
            return __VA_ARGS__;                                                             \
        }                                                                                   \
    }


void copy(miaofs_itr * itr, inc::file & source, void * buffer, uxx buffer_bytes){
    auto rest               = source->length();

    while(rest){
        auto copy_size  = inc::minimum<uxx>(rest, buffer_bytes);
        rest           -= copy_size;

        if (source.read(buffer, copy_size) == not_exist){
            inc::tty.write_line("failed copy \v to miaofs disk", source->path);
            return;
        }

        xmiao_loop(fs.write(xmodify(*itr), buffer, copy_size));
    }
}

void recursive_pc2m(inc::c08 const & miao_path){
    inc::dir{"."}.subdirs().foreach([&](asciis folder){
        inc::dir{folder}.as_cwd();
        auto memory             = inc::temp_allocator{};
        auto alloc              = memory.template item_as<char>();
        auto new_path           = inc::c08("\v/\v/").format(miao_path, folder, alloc);
        {
            auto new_itr        = miaofs_itr(new_path);
            xmiao_loop(fs.open(xmodify(new_itr), inc::open_mode_t::create_when_not_exist));
            fs.close(xmodify(new_itr));
        }
        recursive_pc2m(new_path);
        inc::dir{".."}.as_cwd();
    });

    inc::dir{"."}.subfiles().foreach([&](asciis file_name){
        inc::file source{file_name, inc::access_mode_t::read_only};
        auto memory             = inc::temp_allocator{};
        auto alloc              = memory.template item_as<char>();
        auto new_path           = inc::c08("\v/\v").format(miao_path, file_name, alloc);
        {
            auto new_itr        = miaofs_itr(new_path);
            xmiao_loop(fs.open(xmodify(new_itr), inc::open_mode_t::create_when_not_exist));
            auto buffer         = alloc(max_copy_bytes);
            copy(xmodify(new_itr), source, buffer, max_copy_bytes);
            fs.close(xmodify(new_itr));
        }
    });
}


xinit(inc::the_main){
    if (inc::env::args->length < 2u){
        xhint("params is less then 2");
        return;
    }

    inc::temp_allocator memory;
    auto alloc              = memory.template item_as<char>();
    auto arg_device         = inc::env::args[1];
    // auto context_file       = inc::c08("./\v").format(inc::place_holder::zH{ arg_device }, alloc);
    // xhint(context_file);
    cache_level.disk        = inc::file(arg_device, inc::access_mode_t::read_write);

    if (cache_level.disk->is_open == false){
        inc::tty.write_line("can't access block:", arg_device);
        return;
    }

    auto init_code          = fs.initial();

    if (inc::env::args[0].is_ends_with("/miao.mkfs")){
        if (init_code == inc::miaofs_code_t::success){
            inc::tty.write("miaofs existed in currrent disk, still erase and create y/N?");
            inc::tty.flush();

            xdefer{
                inc::tty.write_line();
            };

            if (inc::tty.read_key(echo)->value != 'y'){
                return;
            }
        }
        auto disk_bytes     = cache_level.disk->length();

        if (disk_bytes < min_disk_bytes){
            inc::tty.write_line("can't make file system, disk:\v size(\v) less 2MiB", arg_device, disk_bytes);
            return;
        }

        if (auto code = fs.make_file_system(inc::miaofs::make_fs_params{ .disk_total_bytes = disk_bytes });
            code != inc::miaofs_code_t::success){
            inc::tty.write_line("file system create failed, code:\v", code);
        }
        else{
            inc::tty.write_line("file system create success");
        }
        return;
    }

    // 后面的命令要求文件系统存在
    if (init_code != inc::miaofs_code_t::success){
        inc::tty.write_line("file initial failed, code:\v", init_code);
        return;
    }

    if (inc::env::args[0].is_ends_with("/miao.pc2m")){
        if (inc::env::args->length < 4u){
            inc::tty.write_line("params missing, usage: miaocp.pc2m /path/to/block /path/miaofs_path /path/pc_path");
            return;
        }

        auto miao_path          = inc::env::args[2];
        auto pc_path            = inc::env::args[3];
        auto is_pc_file         = inc::file(pc_path)->is_exist();
        auto is_pc_dir          = inc::dir(pc_path)->is_exist();

        if (is_pc_file){
            if (miao_path[-1] == '/'){
                auto i_skip     = pc_path.index_of_last({ '/', '\\' });
                auto name       = inc::c08(pc_path).backward(i_skip + 1);
                miao_path       = miao_path.strcat({ "/", name }, alloc);

            }

            miaofs_itr itr(miao_path);
            xmiao_loop(fs.open(xmodify(itr), inc::open_mode_t::create_when_not_exist));
            auto buffer         = alloc(max_copy_bytes);
            auto source         = inc::file(pc_path, inc::access_mode_t::read_only);
            copy(xmodify(itr), source, buffer, max_copy_bytes);
        }
        // 复制整个目录
        else if (is_pc_dir){
            if (miao_path[-1] != '/'){
                inc::tty.write_line("not allowed, requires miaofs path(\v) is a folder which with '/' suffix", miao_path);
                return;
            }
            
            {
                auto i_skip     = pc_path.find_reverse({ '/', '\\' }, 1/*倒数第二个*/);
                auto name       = inc::c08(pc_path).backward(i_skip + 1);
                miao_path       = miao_path.strcat(name, alloc);
                miaofs_itr itr(miao_path);
                xmiao_loop(fs.open(xmodify(itr), inc::open_mode_t::create_when_not_exist));
            }

            if (inc::dir{pc_path}.as_cwd() == bstate_t::fail){
                inc::tty.write_line("can't enter path:\v", pc_path);
                return;
            }
            recursive_pc2m(miao_path);
        }
        else{
            inc::tty.write_line("not allowed, pc path(\v) not existed", pc_path);
        }
    }

    if (inc::env::args[0].is_ends_with("/miao.ls")){
        if (inc::env::args->length < 3u){
            inc::tty.write_line("params missing, usage: miaocp.ls /path/to/block /path/miaofs_path");
            return;
        }

        auto miao_path          = inc::env::args[2];
        miaofs_itr itr(miao_path);
        xmiao_loop(fs.open(xmodify(itr), inc::open_mode_t::create_when_not_exist));

        xmiao_loop(fs.foreach(xmodify(itr), [&](inc::miaofs::foreach_item const & item){
            inc::tty.write_line("\v \v \v \v", 
                item.is_file ? 'f' : 'd',
                item.create_time, 
                inc::place_holder::v{item.file_bytes}.r(15, ' '),
                item.name
            );
        }));
    }
};
